using System;
using System.Collections;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.ServiceModel;
using System.Web.Security;
using dasBlog.Services;
using dasBlog.Services.Contracts.Blogger;
using dasBlog.Services.Contracts.MetaWebLog;
using dasBlog.Services.Contracts.MovableType;
using dasBlog.Storage;
using dasBlog.Storage.Data;
using System.ServiceModel.Security;
using System.Xml;

namespace dasBlog.Services
{

    public interface IBloggerAPIConfig
    {
        bool EnableBloggerApi { get; set; }
        string Title { get; set; }
        string Root { get; set; }
    }

    [ServiceContract]
    public interface IBloggerAPI : IBlogger, IMovableType, IMetaWeblog { };


    public class ConfigStub : IBloggerAPIConfig
    {
        public bool EnableBloggerApi { get; set; }
        public string Title { get; set; }
        public string Root { get; set; }
    }

    public class BloggerAPI : IBloggerAPI
    {
        IBloggerAPIConfig apiConfig;
        
        public BloggerAPI()
        {
            this.apiConfig = new ConfigStub
            {
                EnableBloggerApi = true,
                Title = "Boo",
                Root = "bah"
            };
        }

        public bool blogger_deletePost(string appKey, string postid, string username, string password, bool publish)
        {
            if (!apiConfig.EnableBloggerApi)
            {
                throw new ServiceDisabledFault();
            }

            if (Membership.ValidateUser(username, password) &&
                 Roles.IsUserInRole(username, "admin"))
            {
                new Moniker(postid).Delete();
            }

            return true;
        }

        public bool blogger_editPost(string appKey, string postid, string username, string password, string content, bool publish)
        {
            Moniker postMk = new Moniker(postid);
            if (!apiConfig.EnableBloggerApi)
            {
                throw new ServiceDisabledFault();
            }

            if (Membership.ValidateUser(username, password) &&
                 Roles.IsUserInRole(username, "admin"))
            {
                XmlDocument factory = new XmlDocument();

                TextEntry entry = postMk.Get<TextEntry>();
                if (entry != null)
                {
                    FillEntryFromBloggerPost(entry, content, username);
                    postMk.Store(entry);
                }
            }
            return true;
        }

        public dasBlog.Services.Contracts.Blogger.Category[] blogger_getCategories(string blogid, string username, string password)
        {
            Moniker blogMk = new Moniker(blogid, PathSegmentName.Posts);
            if (!apiConfig.EnableBloggerApi)
            {
                throw new ServiceDisabledFault();
            }

            if (Membership.ValidateUser(username, password) &&
                 Roles.IsUserInRole(username, "admin"))
            {
                return (from PropertyAggregate agg in blogMk.Aggregate(MonikerMatch.Exact, "tag")
                        select new dasBlog.Services.Contracts.Blogger.Category
                        {
                            categoryid = agg.Value,
                            title = agg.Value
                        }).ToArray();

            }
            return null;
        }

        public dasBlog.Services.Contracts.Blogger.Post blogger_getPost(string appKey, string postid, string username, string password)
        {
            Moniker postMk = new Moniker(postid);
            if (!apiConfig.EnableBloggerApi)
            {
                throw new ServiceDisabledFault();
            }

            if (Membership.ValidateUser(username, password) &&
                 Roles.IsUserInRole(username, "admin"))
            {
                TextEntry entry = postMk.Get<TextEntry>();
                if (entry != null)
                {
                    var post = new dasBlog.Services.Contracts.Blogger.Post();
                    FillBloggerPostFromEntry(entry, ref post);
                    return post;
                }
            }
            return null;
        }

        public dasBlog.Services.Contracts.Blogger.Post[] blogger_getRecentPosts(string appKey,
                                                           string blogid,
                                                           string username,
                                                           string password,
                                                           int numberOfPosts)
        {
            Moniker blogMk = new Moniker(blogid, PathSegmentName.Posts);
            if (!apiConfig.EnableBloggerApi)
            {
                throw new ServiceDisabledFault();
            }

            if (Membership.ValidateUser(username, password) &&
                 Roles.IsUserInRole(username, "admin"))
            {
                return (from TextEntry tag in blogMk.SelectAll<TextEntry>()
                        select new dasBlog.Services.Contracts.Blogger.Post
                        {
                            content = tag.Content,
                            dateCreated = tag.Created,
                            postid = tag.Id.ToString(),
                            userid = tag.Author.Url
                        }).ToArray();
            }
            return null;
        }

        public string blogger_getTemplate(string appKey, string blogid, string username, string password, string templateType)
        {
            if (!apiConfig.EnableBloggerApi)
            {
                throw new ServiceDisabledFault();
            }
            if (!Membership.ValidateUser(username, password) ||
                 !Roles.IsUserInRole(username, "admin"))
            {
                throw new SecurityAccessDeniedException();
            }
            return "";
        }

        public dasBlog.Services.Contracts.Blogger.UserInfo blogger_getUserInfo(string appKey, string username, string password)
        {
            if (!apiConfig.EnableBloggerApi)
            {
                throw new ServiceDisabledFault();
            }
            if (!Membership.ValidateUser(username, password) ||
                !Roles.IsUserInRole(username, "admin"))
            {
                throw new SecurityAccessDeniedException();
            }


            MembershipUser user = Membership.GetUser(username);
            dasBlog.Services.Contracts.Blogger.UserInfo userInfo = new dasBlog.Services.Contracts.Blogger.UserInfo();

            userInfo.email = user.Email;
            userInfo.url = apiConfig.Root;
            userInfo.firstname = "";
            userInfo.lastname = "";
            userInfo.nickname = user.UserName;
            return userInfo;
        }

        public dasBlog.Services.Contracts.Blogger.BlogInfo[] blogger_getUsersBlogs(string appKey, string username, string password)
        {
            if (!apiConfig.EnableBloggerApi)
            {
                throw new ServiceDisabledFault();
            }
            if (!Membership.ValidateUser(username, password) ||
                !Roles.IsUserInRole(username, "admin"))
            {
                throw new SecurityAccessDeniedException();
            }

            return (from ScopeDescription scope in StorageBus.Current.GetScopes()
                    select
                        new BlogInfo
                        {
                            blogid = scope.Name,
                            blogName = scope.Name,
                        }).ToArray();
        }

        public string blogger_newPost(
            string appKey,
            string blogid,
            string username,
            string password,
            string content,
            bool publish)
        {
            Moniker blogMk = new Moniker(blogid, PathSegmentName.Posts);
            if (!apiConfig.EnableBloggerApi)
            {
                throw new ServiceDisabledFault();
            }

            if (!Membership.ValidateUser(username, password) ||
                !Roles.IsUserInRole(username, "admin"))
            {
                throw new SecurityAccessDeniedException();
            }

            TextEntry newPost = new TextEntry();
            FillEntryFromBloggerPost(newPost, content, username);
            return blogMk.Store<TextEntry>(newPost).ToString();
        }

        public bool blogger_setTemplate(string appKey, string blogid, string username, string password, string template, string templateType)
        {
            if (!apiConfig.EnableBloggerApi)
            {
                throw new ServiceDisabledFault();
            }
            if (!Membership.ValidateUser(username, password) ||
                 !Roles.IsUserInRole(username, "admin"))
            {
                throw new SecurityAccessDeniedException();
            }
            return false;
        }

        /// <summary>Fills a DasBlog entry from a Blogger Post structure.</summary>
        /// <param name="entry">DasBlog entry to fill.</param>
        /// <param name="content">Content string of the Blogger post</param>
        /// <param name="username">Username of the author</param>
        private void FillEntryFromBloggerPost(TextEntry entry, string content, string username)
        {
            const string TitleStart = "<title>";
            const string TitleStop = "</title>";

            // The title can optionally be sent inline with the content as an XML fragment.  Make sure to parse it out correctly
            // Can't use normal XML processing tools because it's not well formed.  Have to do this the old fashioned way.
            string title = "";
            string lowerContent = content.ToLower();

            int nTitleStart = lowerContent.IndexOf(TitleStart);
            if (nTitleStart >= 0)
            {
                nTitleStart += TitleStart.Length;
                int nTitleStop = lowerContent.IndexOf(TitleStop, nTitleStart);

                title = content.Substring(nTitleStart, nTitleStop - nTitleStart);
                content = content.Substring(nTitleStop + TitleStop.Length);
            }

            //newPost.CreatedUtc = DateTime.Now.ToUniversalTime();

            entry.Title = title;
            entry.Summary = "";
            entry.Content = content;
            entry.Author = new AuthorDescription { DisplayName = username };
        }

        private void FillBloggerPostFromEntry(TextEntry entry, ref dasBlog.Services.Contracts.Blogger.Post post)
        {
            post.content = string.Format("<title>{0}</title>{1}", entry.Title, entry.Content);
            post.dateCreated = entry.Created;
            post.postid = entry.Id.ToString();
            post.userid = entry.Author.Url;
        }

        private dasBlog.Services.Contracts.MovableType.Category InternalGetFrontPageCategory()
        {
            dasBlog.Services.Contracts.MovableType.Category mcat = new dasBlog.Services.Contracts.MovableType.Category();
            mcat.categoryId = "Front Page";
            mcat.categoryName = "Front Page";
            //mcat.isPrimary = true;
            return mcat;
        }

        // MoveableType
        public dasBlog.Services.Contracts.MovableType.Category[] mt_getCategoryList(string blogid, string username, string password)
        {
            Moniker blogMk = new Moniker(blogid, PathSegmentName.Posts);
            if (!apiConfig.EnableBloggerApi)
            {
                throw new ServiceDisabledFault();
            }

            if (Membership.ValidateUser(username, password) &&
                 Roles.IsUserInRole(username, "admin"))
            {
                return (from PropertyAggregate tag in blogMk.Aggregate(MonikerMatch.Exact, "tag")
                        select new dasBlog.Services.Contracts.MovableType.Category
                        {
                            categoryId = tag.Value,
                            categoryName = tag.Value,
                            isPrimary = false
                        }).ToArray();

            }
            return null;

        }

        public dasBlog.Services.Contracts.MovableType.Category[] mt_getPostCategories(string postid, string username, string password)
        {
            Moniker postMk = new Moniker(postid);
            if (!apiConfig.EnableBloggerApi)
            {
                throw new ServiceDisabledFault();
            }

            if (Membership.ValidateUser(username, password) &&
                 Roles.IsUserInRole(username, "admin"))
            {
                TextEntry entry = postMk.Get<TextEntry>();
                if (entry != null)
                {
                    return (from string s in entry.Categories
                            select new dasBlog.Services.Contracts.MovableType.Category
                            {
                                categoryId = s,
                                categoryName = s,
                                isPrimary = false
                            }).ToArray();
                }
            }
            return null;
        }

        public dasBlog.Services.Contracts.MovableType.PostTitle[] mt_getRecentPostTitles(string blogid, string username, string password, int numberOfPosts)
        {
            Moniker blogMk = new Moniker(blogid, PathSegmentName.Posts);
            if (!apiConfig.EnableBloggerApi)
            {
                throw new ServiceDisabledFault();
            }

            if (Membership.ValidateUser(username, password) &&
                 Roles.IsUserInRole(username, "admin"))
            {
                return (from TextEntry tag in blogMk.SelectAll<TextEntry>()
                        select new dasBlog.Services.Contracts.MovableType.PostTitle { title = tag.Title }).ToArray();
            }
            return null;

        }

        public dasBlog.Services.Contracts.MovableType.TrackbackPing[] mt_getTrackbackPings(string postid)
        {
            if (!apiConfig.EnableBloggerApi)
            {
                throw new ServiceDisabledFault();
            }

            Moniker logMk = new Moniker(new Moniker(postid), PathSegmentName.Trackings);
            return (from LogEntry logEntry in logMk.SelectAll<LogEntry>()
                    select new dasBlog.Services.Contracts.MovableType.TrackbackPing 
                    {   
                        pingURL = logEntry.ReferrerUrl,
                        pingIP = logEntry.UserIP,
                        pingTitle = logEntry.ReferrerTitle
                    }).ToArray();
        }

        public bool mt_publishPost(string postid, string username, string password)
        {
            if (!apiConfig.EnableBloggerApi)
            {
                throw new ServiceDisabledFault();
            }

            if (Membership.ValidateUser(username, password) &&
                 Roles.IsUserInRole(username, "admin"))
            {
                Moniker postMk = new Moniker(postid);
                TextEntry entry = postMk.Get<TextEntry>();
                if (entry != null)
                {
                    entry.Published = true;
                    postMk.Store<TextEntry>(entry);
                }
            }
            return true;
        }

        public bool mt_setPostCategories(string postid, string username, string password, dasBlog.Services.Contracts.MovableType.Category[] categories)
        {
            if (!apiConfig.EnableBloggerApi)
            {
                throw new ServiceDisabledFault();
            }

            if (Membership.ValidateUser(username, password) &&
                 Roles.IsUserInRole(username, "admin"))
            {
                Moniker postMk = new Moniker(postid);
                TextEntry entry = postMk.Get<TextEntry>();
                if (entry != null)
                {
                    entry.Categories = (from dasBlog.Services.Contracts.MovableType.Category cat
                                        in categories
                                        select cat.categoryId).ToList();
                    postMk.Store<TextEntry>(entry);
                }
            }
            return true;
        }

        public string[] mt_supportedMethods()
        {
            if (!apiConfig.EnableBloggerApi)
            {
                throw new ServiceDisabledFault();
            }
            ArrayList arrayList = new ArrayList();
            foreach (MethodInfo method in GetType().GetMethods())
            {
                if (method.IsDefined(typeof(OperationContractAttribute), true))
                {
                    OperationContractAttribute attr = method.GetCustomAttributes(typeof(OperationContractAttribute), true)[0] as OperationContractAttribute;
                    arrayList.Add(attr.Action);
                }
            }
            return arrayList.ToArray(typeof(string)) as string[];

        }

        public dasBlog.Services.Contracts.MovableType.TextFilter[] mt_supportedTextFilters()
        {
            if (!apiConfig.EnableBloggerApi)
            {
                throw new ServiceDisabledFault();
            }
            TextFilter tf = new TextFilter();
            tf.key = "default";
            tf.@value = "default";
            return new dasBlog.Services.Contracts.MovableType.TextFilter[] { tf };
        }

        // Metaweblog
        public bool metaweblog_editPost(string postid, string username, string password, dasBlog.Services.Contracts.MetaWebLog.Post post, bool publish)
        {
            if (!apiConfig.EnableBloggerApi)
            {
                throw new ServiceDisabledFault();
            }

            if (Membership.ValidateUser(username, password) &&
                 Roles.IsUserInRole(username, "admin"))
            {
                Moniker postMk = new Moniker(postid);
                TextEntry entry = postMk.Get<TextEntry>();
                if (entry != null)
                {
                    FillEntryFromMetaWeblogPost(entry, post);
                    entry.Published = publish;
                    postMk.Store<TextEntry>(entry);
                }
            }
            return true;
        }

        public dasBlog.Services.Contracts.MetaWebLog.CategoryInfo[] metaweblog_getCategories(string blogid, string username, string password)
        {
            if (!apiConfig.EnableBloggerApi)
            {
                throw new ServiceDisabledFault();
            }

            if (Membership.ValidateUser(username, password) &&
                 Roles.IsUserInRole(username, "admin"))
            {
                //IStorageNode storageNode = StorageBus.Current.FindNode(new Moniker(blogid, PathSegmentName.Tags));
                //return (from PropertyAggregate tag in SerializationTools<PropertyAggregate>.DeserializeList(storageNode.Aggregate(null, new Moniker(new Moniker(blogid), PathSegmentName.Posts).ToString(), MonikerMatch.Exact, "tag", null))
                //        select new dasBlog.Services.Contracts.MetaWebLog.CategoryInfo
                //        {
                //            description = tag.Value
                //        }).ToArray();
            }
            return null;
        }

        public dasBlog.Services.Contracts.MetaWebLog.Post metaweblog_getPost(string postid, string username, string password)
        {
            if (!apiConfig.EnableBloggerApi)
            {
                throw new ServiceDisabledFault();
            }

            if (Membership.ValidateUser(username, password) &&
                 Roles.IsUserInRole(username, "admin"))
            {
                Moniker postMk = new Moniker(postid);
                TextEntry entry = postMk.Get<TextEntry>();
                if (entry != null)
                {
                    var post = new dasBlog.Services.Contracts.MetaWebLog.Post();
                    post.description = entry.Summary;
                    post.dateCreated = entry.Created;
                    post.title = entry.Title;
                    //post.link = post.permalink = SiteUtilities.GetPermaLinkUrl(entry);
                    post.postid = entry.Id.ToString();
                    post.categories = entry.Categories.ToArray();
                    return post;
                }
            }
            return null;
        }

        public dasBlog.Services.Contracts.MetaWebLog.Post[] metaweblog_getRecentPosts(string blogid, string username, string password, int numberOfPosts)
        {
            if (!apiConfig.EnableBloggerApi)
            {
                throw new ServiceDisabledFault();
            }

            if (Membership.ValidateUser(username, password) &&
                 Roles.IsUserInRole(username, "admin"))
            {
                Moniker blogMk = new Moniker(blogid, PathSegmentName.Posts);
                return (from TextEntry entry in blogMk.SelectAll<TextEntry>()
                        select new dasBlog.Services.Contracts.MetaWebLog.Post {
                            description = entry.Content,
                            dateCreated = entry.Created,
                            title = entry.Title,
                            //post.link = post.permalink = SiteUtilities.GetPermaLinkUrl(entry);
                            postid = entry.Id.ToString(),
                            categories = entry.Categories.ToArray()}
                        ).ToArray();
            }
            return null; 
            
        }

        public string metaweblog_newPost(string blogid, string username, string password, dasBlog.Services.Contracts.MetaWebLog.Post post, bool publish)
        {
            if (!apiConfig.EnableBloggerApi)
            {
                throw new ServiceDisabledFault();
            }

            if (!Membership.ValidateUser(username, password) ||
                !Roles.IsUserInRole(username, "admin"))
            {
                throw new SecurityAccessDeniedException();
            }

            Moniker blogMk = new Moniker(blogid, PathSegmentName.Posts);
            TextEntry newPost = new TextEntry();
            FillEntryFromMetaWeblogPost(newPost, post);
            newPost.Published = publish;
            return blogMk.Store<TextEntry>(newPost).ToString();
        }

        /// <summary>Fills a DasBlog entry from a MetaWeblog Post structure.</summary>
        /// <param name="entry">DasBlog entry to fill.</param>
        /// <param name="post">MetaWeblog post structure to fill from.</param>
        /// <returns>TrackbackInfoCollection of posts to send trackback pings.</returns>
        private Collection<LogEntry> FillEntryFromMetaWeblogPost(TextEntry entry, dasBlog.Services.Contracts.MetaWebLog.Post post)
        {
            // W.Bloggar doesn't pass in the DataCreated, 
            // so we have to check for that
            if (post.dateCreated != DateTime.MinValue)
            {
                entry.Created = post.dateCreated.ToUniversalTime();
            }

            entry.Title = post.title;
            entry.Content = post.description;
            entry.Summary = post.mt_excerpt;

            // If mt_allow_comments is null, then the sender did not specify.  Use default dasBlog behavior in that case
            if (post.mt_allow_comments != null)
            {
                //int nAllowComments = Convert.ToInt32(post.mt_allow_comments);
                //if (nAllowComments == 0 || nAllowComments == 2)
                //    entry.AllowComments = false;
                //else
                //    entry.AllowComments = true;
            }

            if (post.categories != null && post.categories.Length > 0)
            {
                post.categories = RemoveDups(post.categories, true);
                entry.Categories = new System.Collections.Generic.List<string>(post.categories);
            }

            // We'll always return at least an empty collection
            var trackbackList = new Collection<LogEntry>();

            // Only MT supports trackbacks in the post
            if (post.mt_tb_ping_urls != null)
            {
                //foreach (string trackbackUrl in post.mt_tb_ping_urls)
                //{
                //    trackbackList.Add(new TrackbackInfo(
                //        trackbackUrl,
                //        SiteUtilities.GetPermaLinkUrl(apiConfig, entry),
                //        entry.Title,
                //        entry.Description,
                //        apiConfig.Title));
                //}
            }
            return trackbackList;
        }

        private string[] RemoveDups(string[] items, bool sort)
        {
            ArrayList noDups = new ArrayList();
            for (int i = 0; i < items.Length; i++)
            {
                if (!noDups.Contains(items[i].Trim()))
                {
                    noDups.Add(items[i].Trim());
                }
            }
            if (sort) noDups.Sort();  //sorts list alphabetically
            string[] uniqueItems = new String[noDups.Count];
            noDups.CopyTo(uniqueItems);
            return uniqueItems;
        }
        /// <summary>
        /// newMediaObject implementation : Xas
        /// </summary>
        /// <param name="blogid"></param>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <param name="enc"></param>
        /// <returns></returns>
        public UrlInfo metaweblog_newMediaObject(string blogid, string username, string password, MediaType enc)
        {
            if (!apiConfig.EnableBloggerApi)
            {
                throw new ServiceDisabledFault();
            }

            if (!Membership.ValidateUser(username, password) ||
                !Roles.IsUserInRole(username, "admin"))
            {
                throw new SecurityAccessDeniedException();
            }

            IStreamStorageNode storageNode = StorageBus.Current.FindNode(new Moniker(blogid, PathSegmentName.Media)) as IStreamStorageNode;
            var result = storageNode.SetStream(new StreamStorageSetRequest
            {
                Stream = new MemoryStream(enc.bits),
                moniker = new Moniker(blogid, PathSegmentName.Media),
                name = enc.name,
                contentType = enc.type
            });
            
            UrlInfo urlInfo = new UrlInfo();
            return new UrlInfo { url = result.StreamInfo.Id.ToString() };
        }
    }
}
